iT邦幫忙

2024 iThome 鐵人賽

DAY 4
0
Security

你的程式真的安全嗎?從資安的角度做 code review系列 第 6

C - use-after-free (記憶體損壞:釋放後使用)

  • 分享至 

  • xImage
  •  

雖然還是很難過沒辦法拿到完賽證明,但我還是會憑著毅力寫完的QQ

下方程式碼片段全部都是擷取自 Secure Code Warrior 線上安全程式培訓平台,因為練習互動時的題目多半不會只有單一個檔案,可能涉及多個檔案、資料夾及多處地方修改,因此我的文章主要是針對最主要的區塊做修改及說明,若有不好理解的地方非常抱歉也還請見諒,也可以實際上去 Secure Code Warrior 玩玩看,搭配著互動,會更有感的學習哦~

C - use-after-free (記憶體損壞:釋放後使用)

  • 形成原因:當記憶體釋放後程式碼重新使用此記憶體位置
  • 後果:指標讀不到記憶體而導致錯誤
  • 實例:
int main(void)
{
    char* ptr = (char*)malloc(SIZE);
    ...
    if(err){
        free(ptr);
    }
    ...
    printf("%s\n",ptr);
}
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

當發生錯誤時釋放記憶體位置的值,但後面又print記體位置導致記憶體損壞

  • 如何防止:確認一旦指標所指向的記憶體被釋放,將所有指標都被設定成null

第一題

錯誤區塊

LocationObject *obj;
{
  auto instance = std::make_unique<LocationObject> ();
  obj = instance.get();
  obj->setCallSign(trackValues.at(1));
  obj->setParameters(stoi(trackValues.at(2)),stoi(trackValues.at(3)),
                     stod(trackValues.at(6)),stoi(trackValues.at(5)),
                     stoi(trackValues.at(4)));
}
tempSituation.push_back(*obj);
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

解釋:
由於unique_ptr中的託管物件存在於範圍內,根據RAII術語語,在呼叫push_back時,指標內的物件將不存在。因此,指標obj也無效。在物件釋出後存取該物件可能會導致程式變得不穩定並崩潰。

主要修正方法

將錯誤區塊改成:

LocationObject obj;
obj.setCallSign(trackValues.at(1));
obj.setParameters(stoi(trackValues.at(2)),stoi(trackValues.at(3)),
                  stod(trackValues.at(6)),stoi(trackValues.at(5)),
                  stoi(trackValues.at(4)));
tempSituation.push_back(obj);
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

解釋:
建議避免使用 heap 結構記憶體,特別是對於 stack 結構記憶體中不需要太多空間的物件。使用的變數儲存在stack 上,函式完成後,變數本身將被刪除,就不會發生 use-after-free 的條件。

第二題

錯誤區塊

accounts.push_back( account );
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

解釋:
accounts是指向同一account 項目的指標向量。 所有向量的值(指標)都指向同一個項目。 在account項目被刪除後,這些複製被使用,將導致記憶體存取無效。

主要修正方法

將以下區塊:

std::vector< Account* > accounts;

Account* account = new Account();
MSXML::IXMLDOMNodePtr user;
while( ( user = users->nextNode() ) != NULL )
{
	bool isName = getNodeValue( user, "name", account->name );
	bool isLogin = getNodeValue( user, "login", account->login );
	bool isPassword = getNodeValue( user, "password", account->password );
	if( !isName || !isLogin || !isPassword )
	{
		return false;
	}
	accounts.push_back( account );
}
delete account;
// Add all user records into database.
for( size_t i = 0; i < accounts.size(); i++ )
{
	Account* account = accounts[ i ];
	std::cout << account->getValues() << std::endl;
	db->usersAdd( account );
}
return true
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

改成:

std::vector< Account > accounts;

MSXML::IXMLDOMNodePtr user;
while( ( user = users->nextNode() ) != NULL )
{
	Account account;
	bool isName = getNodeValue( user, "name", account.name );
	bool isLogin = getNodeValue( user, "login", account.login );
	bool isPassword = getNodeValue( user, "password", account.password );
	if( !isName || !isLogin || !isPassword )
	{
		return false;
	}
	accounts.push_back( account );
}
// Add all user records into database.
for( size_t i = 0; i < accounts.size(); i++ )
{
	Account* account = &accounts[ i ];
	std::cout << account->getValues() << std::endl;
	db->usersAdd( account );
}
return true;
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

解釋:
accounts向量保留真實的account例項,而不僅僅是指標。向量本身控制記憶體分配和重新分配,保護程式免受記憶體洩漏。因此,在這種方法中沒有記憶體洩漏,也沒有釋放的記憶體再次使用。


上一篇
C - null deference (記憶體損壞:空指標引用)
下一篇
C - stack buffer overflow(記憶體損壞:堆疊溢位)
系列文
你的程式真的安全嗎?從資安的角度做 code review18
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言